package com.tanger.es.api;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.github.stuxuhai.jpinyin.ChineseHelper;
import com.github.stuxuhai.jpinyin.PinyinFormat;
import com.github.stuxuhai.jpinyin.PinyinHelper;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.tanger.common.page.PageList;
import com.tanger.common.utils.BeanUtils;
import com.tanger.es.ESConstant;
import com.tanger.es.EsGoods;
import com.tanger.es.GoodsCategory;
import com.tanger.es.inner.Goods;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.index.query.*;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.metrics.ParsedTopHits;
import org.elasticsearch.search.sort.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ESPoiServiceImpl implements ESQueryService, ESCommandService {

    private static final String LANGUAGE_ZH = "zh";
    private static final String LANGUAGE_EN = "en";

    private static final Map<String, Function<GoodsCategory, String>> matchValueFuncMap =
            ImmutableMap.of(LANGUAGE_ZH, GoodsCategory::getCategoryName, LANGUAGE_EN, GoodsCategory::getCategoryEn);
    private static final Map<String, Float> zhBoostMap = new HashMap<String, Float>() {{
        put(ESConstant.FIELD_NAME, 4F);
        put(ESConstant.FIELD_PINYIN_INITIALS, 2F);
        put(ESConstant.FIELD_BRAND_NAME_EN, 0.7F);
//        put(ESConstant.FIELD_CATEGORY2_NAME, 0.8F);
//        put(ESConstant.FIELD_CATEGORY3_NAME, 1.2F);
        put(ESConstant.FIELD_ACTIVITYS_NAME, 1F);
    }};
    private static final Map<String, Float> enBoostMap = new HashMap<String, Float>() {{
        put(ESConstant.FIELD_NAME_EN, 4F);
        put(ESConstant.FIELD_BRAND_NAME_EN, 0.7F);
//        put(ESPoiConstant.FIELD_CATEGORY2_NAME_EN, 0.8F);
//        put(ESPoiConstant.FIELD_CATEGORY3_NAME_EN, 1.2F);
        put(ESConstant.FIELD_ACTIVITYS_NAME_EN, 1F);
    }};
    private static final Map<String, Float> allBoostMap = new HashMap<String, Float>() {{
        putAll(zhBoostMap);
        putAll(enBoostMap);
    }};

    @Autowired
    private EsRepository repository;
    @Autowired
    private ElasticsearchOperations template;
//    @Autowired
//    private GoodsService goodsService;

//    @Override
//    public List<GoodsCategory> findAllCategory(String projectId, String language, GoodsCategory qry) {
//        BoolQueryBuilder builder = QueryBuilders.boolQuery()
//                .must(QueryBuilders.termQuery(ESPoiConstant.FIELD_PROJECT_ID, projectId));
//        if (StringUtils.hasText(qry.getKey())) {
//            if (LANGUAGE_ZH.equals(language)) {
//                builder.must(QueryBuilders.termsQuery(qry.getKey(),
//                        ESConstant.FIELD_CATEGORY_NAME,
//                        ESPoiConstant.FIELD_CATEGORY2_NAME,
//                        ESPoiConstant.FIELD_CATEGORY3_NAME,
//                        ESPoiConstant.FIELD_CATEGORY4_NAME));
//            } else if (LANGUAGE_EN.equals(language)) {
//                builder.must(QueryBuilders.multiMatchQuery(qry.getKey(),
//                        ESPoiConstant.FIELD_CATEGORY1_NAME_EN,
//                        ESPoiConstant.FIELD_CATEGORY2_NAME_EN,
//                        ESPoiConstant.FIELD_CATEGORY3_NAME_EN,
//                        ESPoiConstant.FIELD_CATEGORY4_NAME_EN).operator(Operator.AND));
//            } else {
//                builder.must(QueryBuilders.multiMatchQuery(qry.getKey(),
//                        ESPoiConstant.FIELD_CATEGORY1_NAME,
//                        ESPoiConstant.FIELD_CATEGORY1_NAME_EN,
//                        ESPoiConstant.FIELD_CATEGORY2_NAME,
//                        ESPoiConstant.FIELD_CATEGORY2_NAME_EN,
//                        ESPoiConstant.FIELD_CATEGORY3_NAME,
//                        ESPoiConstant.FIELD_CATEGORY3_NAME_EN,
//                        ESPoiConstant.FIELD_CATEGORY4_NAME,
//                        ESPoiConstant.FIELD_CATEGORY4_NAME_EN)
//                        .operator(Operator.AND));
//            }
//        }
//        NativeSearchQuery query = new NativeSearchQueryBuilder()
//                .withQuery(builder)
//                .withSearchType(SearchType.DEFAULT)
//                .withAggregations(AggregationBuilders.terms(ESPoiConstant.FIELD_CATEGORY1_ID).field(ESPoiConstant.FIELD_CATEGORY1_ID)
//                        .subAggregation(AggregationBuilders.topHits(ESPoiConstant.FIELD_CATEGORY1).size(1))
//                        .size(1000))
//                .withAggregations(AggregationBuilders.terms(ESPoiConstant.FIELD_CATEGORY2_ID).field(ESPoiConstant.FIELD_CATEGORY2_ID)
//                        .subAggregation(AggregationBuilders.topHits(ESPoiConstant.FIELD_CATEGORY2).size(1))
//                        .size(1000))
//                .withAggregations(AggregationBuilders.terms(ESPoiConstant.FIELD_CATEGORY3_ID).field(ESPoiConstant.FIELD_CATEGORY3_ID)
//                        .subAggregation(AggregationBuilders.topHits(ESPoiConstant.FIELD_CATEGORY3).size(1))
//                        .size(1000))
//                .withMaxResults(0)
//                .build();
////        query.setExplain(Boolean.TRUE);
//        List<CategoryDTO> result = new ArrayList<>();
//        SearchHits<ESPoi> searchHits = template.search(query, ESPoi.class);
//        AggregationsContainer<?> aggregationsContainer = searchHits.getAggregations();
//        Aggregations aggregations = (Aggregations)aggregationsContainer.aggregations();
//        Map<String, Aggregation> aggregationMap = aggregations.getAsMap();
//        for (Map.Entry<String, Aggregation> entry : aggregationMap.entrySet()) {
//            ParsedStringTerms aggregation = (ParsedStringTerms)entry.getValue();
//            List<ParsedStringTerms.ParsedBucket> buckets = (List) aggregation.getBuckets();
//            for (ParsedStringTerms.ParsedBucket bucket : buckets) {
//                Aggregations bucketAggregations = bucket.getAggregations();
//                Map<String, Aggregation> bucketAggregationMap = bucketAggregations.getAsMap();
//                for (Map.Entry<String, Aggregation> bucketEntry : bucketAggregationMap.entrySet()) {
//                    String bucketKey = bucketEntry.getKey();
//                    ParsedTopHits topHits = (ParsedTopHits)bucketEntry.getValue();
//                    long totalHits = topHits.getHits().getTotalHits().value;
//                    SearchHit searchHit = topHits.getHits().getAt(0);
//                    Map<String, Object> source = searchHit.getSourceAsMap();
//                    Map<String, String> sourceCategory = (Map<String, String>) source.get(bucketKey);
//                    CategoryDTO category = new CategoryDTO();
//                    category.setId(sourceCategory.get("id"));
//                    category.setName(sourceCategory.get("name"));
//                    category.setNameEn(sourceCategory.get("nameEn"));
//                    category.setPoiCount(totalHits);
//
//                    if (match(language, qry.getKey(), category)) {
//                        result.add(category);
//                    }
//                }
//            }
//        }
//        return result;
//    }

//    private boolean match(String lang, String key, CategoryDTO c) {
//        Function<CategoryDTO, String> func = Optional.ofNullable(lang)
//                .filter(StringUtils::hasText)
//                .map(matchValueFuncMap::get)
//                .orElse(null);
//        if (Objects.nonNull(func)) {
//            return Optional.ofNullable(func.apply(c))
//                    .orElse("")
//                    .contains(key);
//        }
//
//        for (Map.Entry<String, Function<CategoryDTO, String>> entry : matchValueFuncMap.entrySet()) {
//            if (Optional.ofNullable(entry.getValue().apply(c)).orElse("").contains(key)) {
//                return true;
//            }
//        }
//
//        return false;
//    }

    private Map<String, Float> boostMap(String lang) {
        switch (Optional.ofNullable(lang).orElse("")) {
            case LANGUAGE_ZH: return zhBoostMap;
            case LANGUAGE_EN: return enBoostMap;
            default: return allBoostMap;
        }
    }

    private PageList<Goods> queryPoi(String language, QueryVO qry, KeyQueryBuilder keyQueryBuilder) {
        Objects.requireNonNull(Optional.ofNullable(qry.getPageSize()).filter(size -> size > 0).orElse(null), "参数错误");

        //构建商品归属区域查询条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.termQuery(ESConstant.FIELD_AREA_CODE, qry.getAreaCode()));
        if (StringUtils.hasText(qry.getKey())) {
            keyQueryBuilder.build(boolQueryBuilder, qry.getKey(), language, qry.getBoostMap());
        }
        if (!Boolean.TRUE.equals(qry.getAllowNameEmpty())) {
            // TODO existsQuery关键字，查询出某些字段存在（值为null）的条目，
            //  但是不能过滤出值为""的字段,所以在ES存储数据时不要存入空字符串
            boolQueryBuilder.must(QueryBuilders.existsQuery(ESConstant.FIELD_NAME));
        }
        if (Objects.nonNull(qry.getShopId())) {
            // TODO termQuery关键字:精准匹配, matchAllQuery关键字:匹配所有, matchPhraseQuery关键字:对中文精确匹配, matchQuery关键字:单个匹配, multiMatchQuery关键字: 匹配多个字段
            boolQueryBuilder.must(QueryBuilders.termQuery(ESConstant.FIELD_SHOP_ID, qry.getShopId()));
        }
        if (!CollectionUtils.isEmpty(qry.getBrandIds())) {
            boolQueryBuilder.must(QueryBuilders.termsQuery(ESConstant.FIELD_BRAND_ID, qry.getBrandIds()));
        }
        if (StringUtils.hasText(qry.getPinyinInitials())) {
            //单字母搜索
            if (qry.getPinyinInitials().length() == 1) {
                boolQueryBuilder.must(QueryBuilders.termQuery(ESConstant.FIELD_FIRST_LETTER, qry.getPinyinInitials().toLowerCase()));
            } else {
                //TODO 全拼搜索 prefixQuery关键字:模糊搜索
                boolQueryBuilder.must(QueryBuilders.prefixQuery(ESConstant.FIELD_PINYIN_INITIALS_KW, qry.getPinyinInitials().toLowerCase()));
            }
        }

        if (!CollectionUtils.isEmpty(qry.getTypeIds())) {
            // TODO nestedQuery关键字:嵌套查询
            boolQueryBuilder.must(QueryBuilders.nestedQuery(ESConstant.FIELD_CATEGORYS,
                    QueryBuilders.termsQuery(ESConstant.FIELD_CATEGORYS_ID, qry.getTypeIds()), ScoreMode.None));
        }
//        if (StringUtils.hasText(qry.getCategoryId())) {
            //TODO must、should、mustNot组合查询
//            boolQueryBuilder.must(QueryBuilders.boolQuery()
//                    .should(QueryBuilders.termQuery(ESConstant.FIELD_CATEGORY1_ID, qry.getCategoryId()))
//                    .should(QueryBuilders.termQuery(ESConstant.FIELD_CATEGORY2_ID, qry.getCategoryId()))
//                    .should(QueryBuilders.termQuery(ESConstant.FIELD_CATEGORY3_ID, qry.getCategoryId())));
//        }


        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
        nativeSearchQueryBuilder.withPageable(PageRequest.of((int)qry.getPageNum(), (int)qry.getPageSize()));
        // TODO 距离排序 构造填充脚本
//        if (ESConstant.SORT_DISTANCE.equalsIgnoreCase(qry.getSort())) {
//            Map<String, Object> args = ImmutableMap.of("x", qry.getX(), "y", qry.getY(), "f", qry.getFloor(), "h", qry.getHeight());
//            String scriptContent = "Math.sqrt(Math.pow(doc['point.x'].value - params.x, 2) + Math.pow(doc['point.y'].value - params.y, 2)) " +
//                    " + Math.abs(doc['point.floor'].value - params.f) * params.h";
//            Script script = new Script(ScriptType.INLINE,"painless", scriptContent , args);
//            nativeSearchQueryBuilder.withSort(SortBuilders.scriptSort(script, ScriptSortBuilder.ScriptSortType.NUMBER));
//        } else
        // TODO 排序
        if (ESConstant.SORT_EVALUATE_SCORE.equalsIgnoreCase(qry.getSort())) {
            nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(ESConstant.FIELD_EVALUATE_SCORE).order(SortOrder.DESC));
        } else if (ESConstant.SORT_PINYIN_INITIALS.equalsIgnoreCase(qry.getSort())) {
            nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(ESConstant.FIELD_PINYIN_INITIALS_KW).order(SortOrder.ASC));
        } else if (ESConstant.SORT_RECOMMEND.equals(qry.getSort())) {
            NestedSortBuilder nestedSortBuilder = new NestedSortBuilder(ESConstant.FIELD_RECOMMENDS);
            nestedSortBuilder.setFilter(QueryBuilders.termQuery(ESConstant.FIELD_RECOMMENDS_TYPE, qry.getRecommendType()));
            NestedSortBuilder nestedMemberSortBuilder = new NestedSortBuilder(ESConstant.FIELD_RECOMMENDS);
            nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(ESConstant.FIELD_RECOMMENDS_PRIORITY).setNestedSort(nestedSortBuilder));
            nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(ESConstant.FIELD_RECOMMENDS_MEMBER_PRIORITY).setNestedSort(nestedMemberSortBuilder));
        }

        NativeSearchQuery query = nativeSearchQueryBuilder.build();
//        query.setExplain(Boolean.TRUE);
        SearchHits<EsGoods> searchHits = template.search(query, EsGoods.class);
//        log.info("{}", JSON.stringify(searchHits));

        List<Goods> content = Optional.ofNullable(searchHits.getSearchHits())
                .map(s -> s.stream()
                        .map(v -> BeanUtils.copyProperties(v, new Goods()))
                        .collect(Collectors.toList()))
                .orElseGet(Collections::emptyList);

        long totalElem = searchHits.getTotalHits();
        return new PageList(content, qry.getPageNum(), qry.getPageSize(), (totalElem + qry.getPageSize() - 1) / qry.getPageSize());
    }

    private MultiMatchQueryBuilder multiMatchQueryBuilder(String key, Map<String, Float> boostMap) {
        return QueryBuilders.multiMatchQuery(key).fields(boostMap);
    }

    private void matchQueryPoi(BoolQueryBuilder boolQueryBuilder, String key, String language, Map<String, Float> inBoostMap) {
        if (LANGUAGE_ZH.equals(language)) {
            boolQueryBuilder.must(multiMatchQueryBuilder(key, Optional.ofNullable(inBoostMap).orElse(zhBoostMap)).operator(Operator.AND));
        } else if (LANGUAGE_EN.equals(language)) {
            boolQueryBuilder.must(multiMatchQueryBuilder(key, Optional.ofNullable(inBoostMap).orElse(enBoostMap)).operator(Operator.AND));
        } else {
            boolQueryBuilder.must(multiMatchQueryBuilder(key, Optional.ofNullable(inBoostMap).orElse(allBoostMap)).operator(Operator.AND));
        }
    }

    @Override
    public PageList<Goods> matchPhraseQuery(String language, QueryVO qry) {
        return queryPoi(language, qry, this::matchQueryPoi);
    }

    private void multiMatchBestFields(BoolQueryBuilder boolQueryBuilder, String key, String language, Map<String, Float> inBoostMap) {
        if (LANGUAGE_ZH.equals(language)) {
            boolQueryBuilder.must(multiMatchQueryBuilder(key, Optional.ofNullable(inBoostMap).orElse(zhBoostMap)));
        } else if (LANGUAGE_EN.equals(language)) {
            boolQueryBuilder.must(multiMatchQueryBuilder(key, Optional.ofNullable(inBoostMap).orElse(enBoostMap)));
        } else {
            boolQueryBuilder.must(multiMatchQueryBuilder(key, Optional.ofNullable(inBoostMap).orElse(allBoostMap)));
        }
    }

    @Override
    public PageList<Goods> multiMatchBestFields(String language, QueryVO key) {
        return queryPoi(language, key, this::multiMatchBestFields);
    }

    private void multiMatchMostFields(BoolQueryBuilder boolQueryBuilder, String key, String language, Map<String, Float> inBoostMap) {
        if (LANGUAGE_ZH.equals(language)) {
            boolQueryBuilder.must(multiMatchQueryBuilder(key, Optional.ofNullable(inBoostMap).orElse(zhBoostMap)).type(MultiMatchQueryBuilder.Type.MOST_FIELDS));
        } else if (LANGUAGE_EN.equals(language)) {
            boolQueryBuilder.must(multiMatchQueryBuilder(key, Optional.ofNullable(inBoostMap).orElse(enBoostMap)).type(MultiMatchQueryBuilder.Type.MOST_FIELDS));
        } else {
            boolQueryBuilder.must(multiMatchQueryBuilder(key, Optional.ofNullable(inBoostMap).orElse(allBoostMap)).type(MultiMatchQueryBuilder.Type.MOST_FIELDS));
        }
    }

    @Override
    public PageList<Goods> multiMatchMostFields(String language, QueryVO key) {
        return queryPoi(language, key, this::multiMatchMostFields);
    }

    @Override
    public PageList<Goods> multiMatchCrossFields(String language, QueryVO query) {
        return null;
    }

    private void multiMatchCrossFields(BoolQueryBuilder boolQueryBuilder, String key, String language, Map<String, Float> inBoostMap) {
        if (LANGUAGE_ZH.equals(language)) {
            boolQueryBuilder.must(multiMatchQueryBuilder(key, Optional.ofNullable(inBoostMap).orElse(zhBoostMap))
                    .type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)
                    .operator(Operator.AND));
        } else if (LANGUAGE_EN.equals(language)) {
            boolQueryBuilder.must(multiMatchQueryBuilder(key, Optional.ofNullable(inBoostMap).orElse(enBoostMap))
                    .type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)
                    .operator(Operator.AND));
        } else {
            boolQueryBuilder.must(multiMatchQueryBuilder(key, Optional.ofNullable(inBoostMap).orElse(allBoostMap))
                    .type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)
                    .operator(Operator.AND));
        }
    }

    @Override
    public void createAll() {

    }

    @Override
    public void refreshAll() {

    }

    @Override
    public void deleteAll() {

    }

    @Override
    public void tryInitializeAll() {

    }

    @Override
    public void createGoods(Goods goods) {

    }

    @Override
    public void updateGoods(Goods goods) {

    }

    @Override
    public void deleteGoods(Long goodsId) {

    }

    @Override
    public void updateEvaluateScore(Long goodsId) {

    }
//
//    @Override
//    public PageList<Goods> multiMatchCrossFields(String language, QueryVO key) {
//        return queryPoi(language, key, this::multiMatchCrossFields);
//    }
//
//    @Override
//    public void tryInitializeAll(String projectId, String buildingId, String token) {
//        if (!repository.existsById(projectId)) {
//            createAll(projectId, buildingId, token);
//        }
//    }
//
//    @Override
//    public void refreshAll(String projectId, String buildingId, String token) {
//        deleteAll(projectId);
//        createAll(projectId, buildingId, token);
//    }
//
//    @Override
//    public void refreshAllIfBuildingChanged(String projectId, String buildingId, String token) {
//        if (repository.countByProjectIdAndBuilding_Id(projectId, buildingId) == 0L) {
//            refreshAll(projectId, buildingId, token);
//        }
//    }
//
//    @Override
//    public void refreshAll(String buildingId, String token) {
//        List<String> projectIds = projectIdQueryer.findAllProjectIdByBuildingId(buildingId);
//        for (String projectId : projectIds) {
//            refreshAll(projectId, buildingId, token);
//        }
//    }
//
//    @Override
//    public void createAll(String projectId, String buildingId, String token) {
//        BuildingData building = mapDataService.getBuildingData(buildingId);
//        // 地图数据
//        List<MapData> data = mapDataService.getMapData(buildingId);
//        // POI详情
//        Map<String, PoiDetails> poiDetailsMap = poiDetailsService
//                .findPoiDetailsByProjectIdAndBuildingId(projectId, buildingId)
//                .stream()
//                .collect(Collectors.toMap(PoiDetails::getPoiId, Function.identity(), (o, n) -> n));
//        // POI评分
////        Map<String, Double> poiEvaluateScoreMap = poiEvaluationManualMapper.get(projectId, buildingId)
////                .stream()
////                .map(e -> (PoiEvaluationEtc)e)
////                .collect(Collectors.toMap(PoiEvaluationEtc::getPoiId, PoiEvaluationEtc::getAvgScore, (o, n) -> n));
//        Map<String,Double> poiEvaluateScoreMap = gathersPoiService.get(projectId,buildingId)
//                .stream()
//                .map(e -> (PoiEvaluationEtc)e)
//                .collect(Collectors.toMap(PoiEvaluationEtc::getPoiId, PoiEvaluationEtc::getAvgScore, (o, n) -> n));
//
//        // Category
//        Map<String, PoiType> poiTypeParentMap = getPoiTypeParentMap(buildingId, token);
//        // Category同义词
//        Map<String, String> synonymInfoMap = getSynonymMap(buildingId, token);
//        // 关键字推荐
//        Map<String, List<ESKeywordRecommend>> poiKeywordRecommendMap = getPoiKeywordRecommendMap(projectId);
//        // 店铺活动
//        Map<String, ShopActivity> poiShopActivityMap = getShopActivityMap(projectId, buildingId);
//        // 常用推荐项|公共设施
//        Map<String, List<ESGroup>> poiRecommendMap = getPoiRecommendMap(projectId, buildingId);
//        // 特色标签|栏目分类
//        Map<String, List<ESGroup>> poiGroupMap = getPoiGroupMap(projectId, buildingId);
//        //热门分类推荐项(新)
//        Map<String, List<HotGroupMemberDto>> recommendGroupMap = getHotRecommendGroupMap(projectId, buildingId);
//
//        //特色标签|栏目分类(新)
//        Map<String, List<ESGroup>> hotRecommendGroupMap = getHotPoiGroupMap(projectId, buildingId);
//        List<ESPoi> pois = new ArrayList<>(CollectionUtil.countInitialCapacity(data.size()));
//        for (int i = 0; i < data.size(); i++) {
//            MapData d = data.get(i);
//            // 地图数据
//            ESPoi p = new ESPoi();
//            p.setId(id(projectId, d.getPoiId()));
//            p.setProjectId(projectId);
//            p.setPoiId(d.getPoiId());
//            p.setBuilding(new ESNamed(building.getBuildingId(), building.getName(), building.getNameEn()));
//            p.setRegion(new ESNamed(d.getRegionId(), d.getRegionName(), d.getRegionEn()));
//            p.setRoomNumberFromMap(d.getRoomNum());
//            p.setRoomNumber(d.getRoomNum());
//            p.setName(Optional.ofNullable(d.getName()).filter(StringUtils::hasText).orElse(null));
//            p.setNameEn(Optional.ofNullable(d.getNameEn()).filter(StringUtils::hasText).orElse(null));
//            p.setPinyinInitials(getShortPinyin(d.getName()));
//            p.setFirstLetter(getFirstLetter(p.getPinyinInitials()));
////            p.setIcon("https://files.brtbeacon.net/BRTMap/images/" + d.getCategoryId() + "_normal.png");
//            p.setRetrieval(d.getRetrieval());
//            Map<String,String> map = toName(recommendGroupMap,d.getPoiId());
//            p.setCategory4(new ESNamed(map.get("name"),map.get("en")));
//            p.setCategory3(new ESNamed(d.getCategoryId(), d.getCategoryName(), d.getCategoryEn()));
//            p.setCategory2(Optional.ofNullable(poiTypeParentMap.get(d.getCategoryId()))
//                    .map(c -> new ESNamed(c.getValue(), c.getLabel(), c.getEn()))
//                    .orElse(null));
//            p.setCategory1(Optional.ofNullable(poiTypeParentMap.get(d.getCategoryId()))
//                    .map(PoiType::getValue)
//                    .map(poiTypeParentMap::get)
//                    .map(c -> new ESNamed(c.getValue(), c.getLabel(), c.getEn()))
//                    .orElse(null));
//            p.setFloor(new ESNamed(d.getFloorId(), d.getFloorName(), null));
//            p.setPoint(new ESPoint(d.getFloorNumber(), d.getLabelX(), d.getLabelY()));
//            // POI详情
//            PoiDetails details = poiDetailsMap.get(d.getPoiId());
//            if (Objects.nonNull(details)) {
//                p.setIcon(details.getLogo());
//                p.setBusinessHours(new ESInfo(details.getBusinessHours(), details.getBusinessHoursEn()));
//                p.setIntroduction(new ESInfo(details.getIntroduction(), details.getIntroductionEn()));
//                if (StringUtils.hasText(details.getRoomNumber())) {
//                    p.setRoomNumber(details.getRoomNumber());
//                }
//                p.setCustomCategory(new ESNamed(details.getCategory(), details.getCategoryNameEn()));
//                p.setPhone(details.getPhone());
//            }
//            // POI评分
//            p.setEvaluateScore(poiEvaluateScoreMap.get(d.getPoiId()));
//            // Category同义词
//            p.setSynonym(synonymInfoMap.get(d.getCategoryId()));
//            // 关键词推荐
//            p.setKeywordRecommends(poiKeywordRecommendMap.get(d.getPoiId()));
//            // 店铺活动
//            p.setShopActivity(Optional.ofNullable(poiShopActivityMap.get(d.getPoiId()))
//                    .map(this::build)
//                    .orElse(null));
//            // 常用推荐项|公共设施
//            p.setRecommends(poiRecommendMap.get(d.getPoiId()));
//            // 特色标签|栏目分类
//            p.setGroups(poiGroupMap.get(d.getPoiId()));
//            // 特色标签|栏目分类(新)
//            p.setGroupv2s(hotRecommendGroupMap.get(d.getPoiId()));
//            pois.add(p);
//        }
//        repository.saveAll(pois);
//    }
//
//    private Map<String, List<HotGroupMemberDto>> getHotRecommendGroupMap(String projectId, String buildingId) {
//        List<HotGroupMemberDto> dtos =
//                Optional.ofNullable(hotRecommendGroupMapper.findRecommendGroup(projectId,buildingId))
//                .orElseGet(Collections::emptyList);
//        return dtos.stream().collect(Collectors.groupingBy(HotGroupMemberDto::getPoiId));
//    }
//
//    private Map<String,String> toName(Map<String,List<HotGroupMemberDto>> map,String poiId){
//        Iterator<Map.Entry<String,List<HotGroupMemberDto>>> iterator = map.entrySet().iterator();
//        StringBuilder nameBuilder = new StringBuilder();
//        StringBuilder enBuilder = new StringBuilder();
//        while (iterator.hasNext()) {
//            Map.Entry<String,List<HotGroupMemberDto>> group = iterator.next();
//            if (poiId.equals(group.getKey())) {
//                List<HotGroupMemberDto> list = group.getValue();
//                for (int i = 0; i < list.size(); i++) {
//                    if (!UtilString.isNull(list.get(i).getName())) {
//                        nameBuilder.append(list.get(i).getName()).append(i == list.size()-1?"":":");
//                    }
//                    if (!UtilString.isNull(list.get(i).getEn())) {
//                        enBuilder.append(list.get(i).getEn()).append(i == list.size()-1?"":":");
//                    }
//                }
//            }
//        }
//        Map<String,String> result = new HashMap<>();
//        result.put("name",nameBuilder.toString());
//        result.put("en",enBuilder.toString());
//        return result;
//    }
//
//    @Override
//    public void update(String projectId, String buildingId, String poiId, ESPoi esPoi) {
//        ESPoi entity = repository.findById(String.join(projectId, buildingId, poiId))
//                .orElseThrow(() -> new BRTException("暂无此POI"));
//        Beans.copyProperties(esPoi, entity);
//        repository.save(entity);
//    }
//
//    @Override
//    public void createRecommendGroup(Long groupId) {
////        HotRecommend group = hotRecommendMapper.selectById(groupId);
////        if (Objects.isNull(group)) {
////            log.warn("[ES][POI] group not exist when group opened. groupId:{}", groupId);
////            return;
////        }
////        if (!Objects.equals(group.getIsOpen(), 1)) {
////            log.warn("[ES][POI] group state error when group opened. groupId:{}", groupId);
////            return;
////        }
////
////        Map<String, PoiGroupMember> memberMap = groupMemberMapper.selectList(new QueryWrapper<PoiGroupMember>()
////                        .select("id", "poi_id", "priority")
////                        .eq("group_id", groupId))
////                .stream()
////                .collect(Collectors.toMap(PoiGroupMember::getPoiId, Function.identity(), (o, n) -> n));
////
////        addGroupMember(group, memberMap);
//    }
//
//    private Map<String, List<ESGroup>> getHotPoiGroupMap(String projectId, String buildingId) {
//        List<HotRecommendGroup> groups = hotRecommendGroupMapper.selectList(new QueryWrapper<HotRecommendGroup>()
//                .select("id", "name", "en", "type", "priority")
//                .eq("project_id", projectId)
//                .eq("activated", 1));
//        if (CollectionUtil.isEmpty(groups)) {
//            return Collections.emptyMap();
//        }
//        Map<Long, HotRecommendGroup> groupMap = groups.stream()
//                .collect(Collectors.toMap(HotRecommendGroup::getId, Function.identity()));
//        List<HotGroupMember> groupMembers = hotGroupMemberMapper.selectList(new QueryWrapper<HotGroupMember>()
//                .select("id", "group_id","poi_id", "priority")
//                .in("group_id", groupMap.keySet()));
//        long poiCount = groupMembers.stream()
//                .map(HotGroupMember::getPoiId)
//                .filter(StringUtils::hasText)
//                .count();
//        Map<String, List<ESGroup>> result = new HashMap<>(CollectionUtil.countInitialCapacity((int)poiCount));
//        for (int i = 0; i < groupMembers.size(); i++) {
//            HotGroupMember p = groupMembers.get(i);
//            HotRecommendGroup r = groupMap.get(p.getGroupId());
//            if (Objects.isNull(r)) {
//                continue;
//            }
//
//            ESGroup g = new ESGroup();
//            g.setId(r.getId().toString());
//            g.setName(r.getName());
//            g.setNameEn(r.getEn());
//            g.setType(r.getType().intValue() == 4? "1":"0");
//            g.setPriority(r.getPriority());
//            g.setMemberId(p.getId().toString());
//            g.setMemberPriority(p.getPriority());
//
//            result.computeIfAbsent(p.getPoiId(), k -> new ArrayList<>(groups.size()))
//                    .add(g);
//        }
//        return result;
//    }
//
//    @Override
//    public void updateHotGroupMembers(String name, String en, List<String> poiIds, String productId, String buildingId) {
//        // 特色标签|栏目分类
//        Map<String, List<ESGroup>> poiGroupMap = getHotPoiGroupMap(productId, buildingId);
//        update(QueryBuilders.boolQuery()
//                        .must(QueryBuilders.termQuery(ESPoiConstant.FIELD_PROJECT_ID, productId))
//                        .must(QueryBuilders.termsQuery(ESPoiConstant.FIELD_POI_ID, poiIds)),
//                esPoi -> {
//                    if (Objects.isNull(esPoi.getCategory4())) {
//                        esPoi.setCategory4(new ESNamed(name,en));
//                    } else {
//                        ESNamed esName = esPoi.getCategory4();
//                        if (!esName.getName().contains(name)) {
//                            esName.setName(convert(esName.getName(), name));
//                            esName.setNameEn(convert(esName.getNameEn(), en));
//                            esPoi.setCategory4(esName);
//                        }
//                    }
//                    esPoi.setGroupv2s(poiGroupMap.get(esPoi.getPoiId()));
//                });
//    }
//
//    private String convert(String orgName,String name){
//        String newName = "";
//        if (orgName.endsWith(":")) {
//            newName = orgName+name;
//        }else {
//            newName = orgName+":"+name;
//        }
//        return newName;
//    }
//
//    @Override
//    public void saveHotGroupMembers(String name, String en, List<String> poiIds, List<String> reduceIdList, String productId, Long groupId,String buildingId) {
//        if (poiIds.size() > 0) {
//            updateHotGroupMembers(name,en,poiIds,productId,buildingId);
//        }
//        // 特色标签|栏目分类
//        Map<String, List<ESGroup>> poiGroupMap = getHotPoiGroupMap(productId, buildingId);
//        if (reduceIdList.size() > 0) {
//            update(QueryBuilders.boolQuery()
//                            .must(QueryBuilders.termQuery(ESPoiConstant.FIELD_PROJECT_ID, productId))
//                            .must(QueryBuilders.termsQuery(ESPoiConstant.FIELD_POI_ID, reduceIdList)),
//                    esPoi -> {
//                        if (Objects.nonNull(esPoi.getCategory4())) {
//                            esPoi = replace(esPoi, name, en);
//                        }
//                        esPoi.setGroupv2s(poiGroupMap.get(esPoi.getPoiId()));
//                    });
//        }
//    }
//
//    @Override
//    public void moveHotGroupMembers(String projectId, List<Long> groupIds, String buildingId) {
//        // 特色标签|栏目分类
//        Map<String, List<ESGroup>> poiGroupMap = getHotPoiGroupMap(projectId, buildingId);
//        if (!CollectionUtils.isEmpty(groupIds) && poiGroupMap != null) {
//            update(QueryBuilders.boolQuery()
//                            .must(QueryBuilders.termQuery(ESPoiConstant.FIELD_PROJECT_ID, projectId))
//                            .must(QueryBuilders.termsQuery(ESPoiConstant.FIELD_POI_ID, poiGroupMap.keySet())),
//                    esPoi -> {
//                        esPoi.setGroupv2s(poiGroupMap.get(esPoi.getPoiId()));
//                    });
//        }
//    }
//
//    private ESPoi replace(ESPoi esPoi,String name,String en){
//        ESNamed esName = esPoi.getCategory4();
//        if (esName.getName().contains(name)) {
//            String realName = esName.getName().replace(":"+name,"");
//            realName = esName.getName().replace(name,"");
//            esName.setName(realName);
//        }
//        if (esName.getNameEn().contains(en)) {
//            String realEn = esName.getNameEn().replace(":"+en,"");
//            realEn = esName.getNameEn().replace(en,"");
//            esName.setNameEn(realEn);
//        }
//        esPoi.setCategory4(esName);
//        return esPoi;
//    }
//
//    @Override
//    public void updatePoiDetails(PoiDetails poiDetails) {
//        ESPoi entity = repository.findById(id(poiDetails.getProductId(), poiDetails.getPoiId())).orElse(null);
//        if (Objects.isNull(entity)) {
//            log.warn("[ES][POI] espoi is null when poi details updated. projectId:{} buildingId:{}, poiId:{}",
//                    poiDetails.getProductId(), poiDetails.getBuildingId(), poiDetails.getPoiId());
//            return;
//        }
//
//        entity.setIcon(poiDetails.getLogo());
//        entity.setBusinessHours(new ESInfo(poiDetails.getBusinessHours(), poiDetails.getBusinessHoursEn()));
//        entity.setIntroduction(new ESInfo(poiDetails.getIntroduction(), poiDetails.getIntroductionEn()));
//        entity.setRoomNumber(Optional.ofNullable(poiDetails.getRoomNumber())
//                .filter(StringUtils::hasText)
//                .orElse(entity.getRoomNumberFromMap()));
//        entity.setCustomCategory(new ESNamed(poiDetails.getCategory(), poiDetails.getCategoryNameEn()));
//        entity.setPhone(poiDetails.getPhone());
//
//        repository.save(entity);
//    }
//
//    @Override
//    public void updatePoiEvaluateScore(String projectId, String buildingId, String poiId) {
//        ESPoi entity = repository.findById(id(projectId, poiId)).orElse(null);
//        if (Objects.isNull(entity)) {
//            log.warn("[ES][POI] espoi is null when poi evaluate score changed. projectId:{} buildingId:{}, poiId:{}",
//                    projectId, buildingId, poiId);
//            return;
//        }
//        entity.setEvaluateScore(gathersPoiService.getAvgScore(projectId,buildingId,poiId));
////        entity.setEvaluateScore(poiEvaluationManualMapper.getAvgScore(projectId, buildingId, poiId));
//    }
//
//    @Override
//    public void updateKeywordRecommend(String projectId, Long keywordRecommendId) {
//        RankingRecommendationEntity entity = rankingRecommendationRepository.findById(keywordRecommendId).orElse(null);
//        if (Objects.isNull(entity)) {
//            return;
//        }
//        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
//                .must(QueryBuilders.termQuery(ESPoiConstant.FIELD_PROJECT_ID, entity.getProjectId()))
//                .must(QueryBuilders.nestedQuery(ESPoiConstant.FIELD_KEYWORD_RECOMMENDS,
//                        QueryBuilders.termQuery(ESPoiConstant.FIELD_KEYWORD_RECOMMENDS_ID, entity.getId()), ScoreMode.None));
//        String skrId = entity.getId().toString();
//        List<String> keywords = Arrays.asList(entity.getKeyWords());
//        update(queryBuilder, p ->
//                p.getKeywordRecommends().forEach(r -> {
//                    if (Objects.equals(r.getId(), skrId)) {
//                        r.setKeywords(keywords);
//                    }
//                }));
//    }
//
//    @Override
//    public void removeKeywordRecommend(String projectId, Long keywordRecommendId) {
//        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
//                .must(QueryBuilders.termQuery(ESPoiConstant.FIELD_PROJECT_ID, projectId))
//                .must(QueryBuilders.nestedQuery(ESPoiConstant.FIELD_KEYWORD_RECOMMENDS,
//                        QueryBuilders.termQuery(ESPoiConstant.FIELD_KEYWORD_RECOMMENDS_ID, keywordRecommendId), ScoreMode.None));
//        String skrId = keywordRecommendId.toString();
//        update(queryBuilder, p -> p.setKeywordRecommends(
//                p.getKeywordRecommends()
//                        .stream()
//                        .filter(e -> !Objects.equals(e.getId(), skrId))
//                        .collect(Collectors.toList())));
//    }
//
//    @Override
//    public void createKeywordRecommendPois(String projectId, Long keywordRecommendId, List<String> poiIds) {
//        RankingRecommendationEntity entity = rankingRecommendationRepository.findById(keywordRecommendId).orElse(null);
//        if (Objects.isNull(entity)) {
//            return;
//        }
//        Map<String, Integer> poiPriorityMap = getKeywordRecommendPoiPriorityMap(keywordRecommendId, poiIds);
//        if (poiPriorityMap.isEmpty()) {
//            return;
//        }
//
//        List<String> keywords = Arrays.asList(entity.getKeyWords());
//        List<ESPoi> pois = Lists.newArrayList(repository.findAllById(poiPriorityMap.keySet()
//                .stream()
//                .map(poiId -> id(projectId, poiId))
//                .collect(Collectors.toList())));
//        for (ESPoi poi : pois) {
//            ESKeywordRecommend r = new ESKeywordRecommend(keywordRecommendId.toString(), keywords, poiPriorityMap.get(poi.getPoiId()));
//            if (Objects.nonNull(poi.getKeywordRecommends())) {
//                poi.getKeywordRecommends().add(r);
//            } else {
//                poi.setKeywordRecommends(Arrays.asList(r));
//            }
//        }
//
//        repository.saveAll(pois);
//    }
//
//    @Override
//    public void updateKeywordRecommendPois(String projectId, Long keywordRecommendId, List<String> poiIds) {
//        Map<String, Integer> poiPriorityMap = getKeywordRecommendPoiPriorityMap(keywordRecommendId, poiIds);
//        String skrId = keywordRecommendId.toString();
//        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
//                .must(QueryBuilders.termQuery(ESPoiConstant.FIELD_PROJECT_ID, projectId))
//                .must(QueryBuilders.termsQuery(ESPoiConstant.FIELD_POI_ID, poiPriorityMap.keySet()))
//                .must(QueryBuilders.nestedQuery(ESPoiConstant.FIELD_KEYWORD_RECOMMENDS,
//                        QueryBuilders.termQuery(ESPoiConstant.FIELD_KEYWORD_RECOMMENDS_ID, skrId), ScoreMode.None));
//        update(queryBuilder, esPoi -> {
//            esPoi.getKeywordRecommends().forEach(item -> {
//                if (Objects.equals(skrId, item.getId())) {
//                    item.setPriority(poiPriorityMap.get(esPoi.getPoiId()));
//                }
//            });
//        });
//    }
//
//    @Override
//    public void deleteKeyWordRecommendPois(String projectId, Long keywordRecommendId, List<String> poiIds) {
//        String skrId = keywordRecommendId.toString();
//        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
//                .must(QueryBuilders.termQuery(ESPoiConstant.FIELD_PROJECT_ID, projectId))
//                .must(QueryBuilders.termsQuery(ESPoiConstant.FIELD_POI_ID, poiIds))
//                .must(QueryBuilders.nestedQuery(ESPoiConstant.FIELD_KEYWORD_RECOMMENDS,
//                        QueryBuilders.termQuery(ESPoiConstant.FIELD_KEYWORD_RECOMMENDS_ID, skrId), ScoreMode.None));
//        update(queryBuilder, esPoi -> esPoi.setKeywordRecommends(esPoi.getKeywordRecommends()
//                .stream()
//                .filter(e -> !Objects.equals(e.getId(), skrId))
//                .collect(Collectors.toList())));
//    }
//
//    @Override
//    public void createPoiGroup(Long groupId) {
//        PoiGroup group = groupMapper.selectById(groupId);
//        if (Objects.isNull(group)) {
//            log.warn("[ES][POI] group not exist when group opened. groupId:{}", groupId);
//            return;
//        }
//        if (!Objects.equals(group.getActivated(), 1)) {
//            log.warn("[ES][POI] group state error when group opened. groupId:{}", groupId);
//            return;
//        }
//
//        Map<String, PoiGroupMember> memberMap = groupMemberMapper.selectList(new QueryWrapper<PoiGroupMember>()
//                .select("id", "poi_id", "priority")
//                .eq("group_id", groupId))
//                .stream()
//                .collect(Collectors.toMap(PoiGroupMember::getPoiId, Function.identity(), (o, n) -> n));
//
//        addGroupMember(group, memberMap);
//    }
//
//    @Override
//    public void updatePoiGroups(List<Long> groupIds) {
//        Map<String, PoiGroup> poiGroupMap = groupMapper.selectBatchIds(groupIds)
//                .stream()
//                .collect(Collectors.toMap(g -> g.getId().toString(), Function.identity()));
//        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
//                .must(QueryBuilders.nestedQuery(ESPoiConstant.FIELD_GROUPS,
//                        QueryBuilders.termsQuery(ESPoiConstant.FIELD_GROUPS_ID, poiGroupMap.keySet()), ScoreMode.None));
//        update(queryBuilder, esPoi -> esPoi.getGroups().forEach(item -> {
//            PoiGroup pg = poiGroupMap.get(item.getId());
//            if (Objects.nonNull(pg)) {
//                item.setName(pg.getName());
//                item.setNameEn(pg.getEn());
//                item.setType(pg.getType().toString());
//                item.setPriority(pg.getPriority());
//            }
//        }));
//    }
//
//    @Override
//    public void deletePoiGroups(List<Long> groupIds) {
//        Set<String> esGroupIdSet = groupIds.stream()
//                .map(Object::toString)
//                .collect(Collectors.toSet());
//        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
//                .must(QueryBuilders.nestedQuery(ESPoiConstant.FIELD_GROUPS,
//                        QueryBuilders.termsQuery(ESPoiConstant.FIELD_GROUPS_ID, esGroupIdSet), ScoreMode.None));
//        update(queryBuilder, esPoi -> esPoi.setGroups(
//                esPoi.getGroups()
//                        .stream()
//                        .filter(g -> !esGroupIdSet.contains(g.getId()))
//                        .collect(Collectors.toList())));
//    }
//
//    @Override
//    public void createPoiGroupMembers(Long groupId, List<String> poiIds) {
//        PoiGroup group = groupMapper.selectById(groupId);
//        if (Objects.isNull(group)) {
//            log.warn("[ES][POI] group not exist when group opened. groupId:{}", groupId);
//            return;
//        }
//        if (!Objects.equals(group.getActivated(), 1)) {
//            return;
//        }
//
//        Map<String, PoiGroupMember> memberMap = groupMemberMapper.selectList(new QueryWrapper<PoiGroupMember>()
//                        .select("id", "poi_id", "priority")
//                        .eq("group_id", groupId)
//                        .in("poi_id", poiIds))
//                .stream()
//                .collect(Collectors.toMap(PoiGroupMember::getPoiId, Function.identity()));
//
//        addGroupMember(group, memberMap);
//    }
//
//    @Override
//    public void updatePoiGroupMembers(List<Long> groupMemberIds) {
//        Map<String, PoiGroupMember> memberMap = groupMemberMapper.selectList(new QueryWrapper<PoiGroupMember>()
//                        .select("id", "priority")
//                        .in("id", groupMemberIds))
//                .stream()
//                .collect(Collectors.toMap(m -> m.getId().toString(), Function.identity()));
//
//        update(QueryBuilders.boolQuery()
//                .must(QueryBuilders.nestedQuery(ESPoiConstant.FIELD_GROUPS,
//                        QueryBuilders.termsQuery(ESPoiConstant.FIELD_GROUPS_MEMBER_ID, memberMap.keySet()), ScoreMode.None)),
//                esPoi -> esPoi.getGroups().forEach(item -> Optional.ofNullable(memberMap.get(item.getMemberId()))
//                        .map(PoiGroupMember::getPriority)
//                        .ifPresent(item::setMemberPriority)));
//    }
//
//    @Override
//    public void deletePoiGroupMembers(List<Long> groupMemberIds) {
//        Set<String> esGroupMemberIdSet = groupMemberIds.stream().map(Object::toString).collect(Collectors.toSet());
//        update(QueryBuilders.boolQuery()
//                        .must(QueryBuilders.nestedQuery(ESPoiConstant.FIELD_GROUPS,
//                                QueryBuilders.termsQuery(ESPoiConstant.FIELD_GROUPS_MEMBER_ID, esGroupMemberIdSet), ScoreMode.None)),
//                esPoi -> esPoi.setGroups(esPoi.getGroups().stream()
//                                .filter(g -> !esGroupMemberIdSet.contains(g.getMemberId()))
//                                .collect(Collectors.toList())));
//    }
//
//    @Override
//    public void createRecommend(Long recommendId) {
//        Recommend recommend = recommendMapper.selectById(recommendId);
//        if (Objects.isNull(recommend)) {
//            log.warn("[ES][POI] recommend not exist when recommend opened. recommendId:{}", recommendId);
//            return;
//        }
//        if (!Objects.equals(recommend.getIsOpen(), 1)) {
//            log.warn("[ES][POI] recommend state error when recommend opened. recommendId:{}", recommendId);
//            return;
//        }
//
//        Map<String, RecommendPoi> memberMap = recommendPoiMapper.selectList(new QueryWrapper<RecommendPoi>()
//                        .select("id", "poi_id", "priority")
//                        .eq("recommend_id", recommendId))
//                .stream()
//                .collect(Collectors.toMap(RecommendPoi::getPoiId, Function.identity(), (o, n) -> n));
//
//        addRecommendPoi(recommend, memberMap);
//    }
//
//    @Override
//    public void updateRecommends(List<Long> recommendIds) {
//        Map<String, Recommend> recommendMap = recommendMapper.selectBatchIds(recommendIds)
//                .stream()
//                .collect(Collectors.toMap(g -> g.getId().toString(), Function.identity()));
//        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
//                .must(QueryBuilders.nestedQuery(ESPoiConstant.FIELD_GROUPS,
//                        QueryBuilders.termsQuery(ESPoiConstant.FIELD_GROUPS_ID, recommendMap.keySet()), ScoreMode.None));
//        update(queryBuilder, esPoi -> esPoi.getRecommends().forEach(item -> {
//            Recommend pg = recommendMap.get(item.getId());
//            if (Objects.nonNull(pg)) {
//                item.setName(pg.getName());
//                item.setNameEn(pg.getNameEn());
//                item.setType(pg.getType().toString());
//                item.setPriority(pg.getPriority());
//            }
//        }));
//    }
//
//    @Override
//    public void deleteRecommends(List<Long> recommendIds) {
//        Set<String> esRecommendIdSet = recommendIds.stream()
//                .map(Object::toString)
//                .collect(Collectors.toSet());
//        update(QueryBuilders.boolQuery()
//                        .must(QueryBuilders.nestedQuery(ESPoiConstant.FIELD_RECOMMENDS,
//                                QueryBuilders.termsQuery(ESPoiConstant.FIELD_RECOMMENDS_ID, esRecommendIdSet), ScoreMode.None)),
//                esPoi -> esPoi.setRecommends(
//                        esPoi.getRecommends()
//                                .stream()
//                                .filter(g -> !esRecommendIdSet.contains(g.getId()))
//                                .collect(Collectors.toList())));
//    }
//
//    @Override
//    public void createRecommendPois(Long recommendId, List<String> poiIds) {
//        Recommend recommend = recommendMapper.selectById(recommendId);
//        if (Objects.isNull(recommend)) {
//            log.warn("[ES][POI] recommend not exist when recommend opened. recommendId:{}", recommendId);
//            return;
//        }
//        if (!Objects.equals(recommend.getIsOpen(), 1)) {
//            return;
//        }
//
//        Map<String, RecommendPoi> memberMap = recommendPoiMapper.selectList(new QueryWrapper<RecommendPoi>()
//                        .select("id", "poi_id", "priority")
//                        .eq("recommend_id", recommendId)
//                        .in("poi_id", poiIds))
//                .stream()
//                .collect(Collectors.toMap(RecommendPoi::getPoiId, Function.identity()));
//
//        addRecommendPoi(recommend, memberMap);
//    }
//
//    @Override
//    public void updateRecommendPois(List<Long> recommendPoiIds) {
//        Map<String, RecommendPoi> memberMap = recommendPoiMapper.selectList(new QueryWrapper<RecommendPoi>()
//                        .select("id", "priority")
//                        .in("id", recommendPoiIds))
//                .stream()
//                .collect(Collectors.toMap(m -> m.getId().toString(), Function.identity()));
//
//        update(QueryBuilders.boolQuery()
//                        .must(QueryBuilders.nestedQuery(ESPoiConstant.FIELD_RECOMMENDS,
//                                QueryBuilders.termsQuery(ESPoiConstant.FIELD_RECOMMENDS_MEMBER_ID, memberMap.keySet()), ScoreMode.None)),
//                esPoi -> esPoi.getRecommends().forEach(item -> Optional.ofNullable(memberMap.get(item.getMemberId()))
//                        .map(RecommendPoi::getPriority)
//                        .ifPresent(item::setMemberPriority)));
//    }
//
//    @Override
//    public void deleteRecommendPois(Long recommendId, List<String> poiIds) {
//        String srId = recommendId.toString();
//        update(QueryBuilders.boolQuery()
//                        .must(QueryBuilders.termsQuery(ESPoiConstant.FIELD_POI_ID, poiIds))
//                        .must(QueryBuilders.nestedQuery(ESPoiConstant.FIELD_RECOMMENDS,
//                                QueryBuilders.termQuery(ESPoiConstant.FIELD_RECOMMENDS_ID, recommendId), ScoreMode.None)),
//                esPoi -> esPoi.setRecommends(esPoi.getRecommends().stream()
//                        .filter(g -> !Objects.equals(srId, g.getId()))
//                        .collect(Collectors.toList())));
//    }
//
//    @Override
//    public void addShopActivity(String projectId, String buildingId) {
//        // 店铺活动
//        Map<String, ShopActivity> poiShopActivityMap = getShopActivityMap(projectId, buildingId);
//        update(QueryBuilders.boolQuery()
//                .must(QueryBuilders.termQuery(ESPoiConstant.FIELD_PROJECT_ID, projectId))
//                .must(QueryBuilders.termsQuery(ESPoiConstant.FIELD_POI_ID, poiShopActivityMap.keySet())),
//                esPoi -> esPoi.setShopActivity(Optional.ofNullable(poiShopActivityMap.get(esPoi.getPoiId()))
//                        .map(this::build)
//                        .orElse(null)));
//    }
//
//    @Override
//    public void removeShopActivity(String projectId, String buildingId) {
//        update(QueryBuilders.boolQuery()
//                .must(QueryBuilders.termsQuery(ESPoiConstant.FIELD_PROJECT_ID, projectId))
//                .must(QueryBuilders.existsQuery(ESPoiConstant.FIELD_SHOP_ACTIVITY)),
//                esPoi -> esPoi.setShopActivity(null));
//    }
//
//    @Override
//    public void createShopActivity(Long shopActivityId) {
//        ShopActivity shopActivity = shopActivityService.findOne(shopActivityId);
//        if (Objects.isNull(shopActivity)) {
//            log.warn("[ES][POI] find shop activity fail, when created. shopActivityId:{} ", shopActivityId);
//            return;
//        }
//        if (!shopActivityService.storeActivityIsEnable(shopActivity.getProductId(), shopActivity.getBuildingId())) {
//            return;
//        }
//        ESPoi esPoi = repository.findById(id(shopActivity.getProductId(), shopActivity.getPoiId())).orElse(null);
//        if (Objects.isNull(esPoi)) {
//            log.warn("[ES][POI] find es poi fail, when shop activity created. shopActivityId:{} poiId:{}", shopActivityId, shopActivity.getPoiId());
//            return;
//        }
//        esPoi.setShopActivity(build(shopActivity));
//        repository.save(esPoi);
//    }
//
//    @Override
//    public void updateShopActivity(Long shopActivityId) {
//        ShopActivity shopActivity = shopActivityService.findOne(shopActivityId);
//        if (Objects.isNull(shopActivity)) {
//            log.warn("[ES][POI] find shop activity fail, when created. shopActivityId:{} ", shopActivityId);
//            return;
//        }
//
//        ESPoi esPoi = repository.findById(id(shopActivity.getProductId(), shopActivity.getPoiId())).orElse(null);
//        if (Objects.isNull(esPoi)) {
//            log.warn("[ES][POI] find es poi fail, when shop activity created. shopActivityId:{} poiId:{}", shopActivityId, shopActivity.getPoiId());
//            return;
//        }
//        if (shopActivityService.storeActivityIsEnable(shopActivity.getProductId(), shopActivity.getBuildingId()) &&
//                Objects.equals(shopActivity.getActivated(), 1))
//        {
//            esPoi.setShopActivity(build(shopActivity));
//        } else {
//            esPoi.setShopActivity(null);
//        }
//        repository.save(esPoi);
//    }
//
//    @Override
//    public void deleteShopActivity(List<Long> shopActivityIds) {
//        update(QueryBuilders.termsQuery(ESPoiConstant.FIELD_SHOP_ACTIVITY_ID,
//                        shopActivityIds.stream().map(Object::toString).collect(Collectors.toList())),
//                esPoi -> esPoi.setShopActivity(null));
//    }
//
//    @Override
//    public void deleteAll(String projectId) {
//        repository.deleteAllByProjectId(projectId);
//    }
//
//    private void addRecommendPoi(Recommend recommend, Map<String, RecommendPoi> memberMap) {
//        update(QueryBuilders.boolQuery()
//                        .must(QueryBuilders.termQuery(ESPoiConstant.FIELD_PROJECT_ID, recommend.getProductId()))
//                        .must(QueryBuilders.termsQuery(ESPoiConstant.FIELD_POI_ID, memberMap.keySet())),
//                esPoi -> {
//                    ESGroup esGroup = new ESGroup();
//                    esGroup.setId(recommend.getId().toString());
//                    esGroup.setName(recommend.getName());
//                    esGroup.setNameEn(recommend.getNameEn());
//                    esGroup.setType(recommend.getType().toString());
//                    esGroup.setPriority(recommend.getPriority());
//                    RecommendPoi m = memberMap.get(esPoi.getPoiId());
//                    esGroup.setMemberId(m.getId().toString());
//                    esGroup.setMemberPriority(m.getPriority());
//                    if (Objects.isNull(esPoi.getGroups())) {
//                        esPoi.setRecommends(Arrays.asList(esGroup));
//                    } else {
//                        esPoi.getRecommends().add(esGroup);
//                    }
//                });
//    }
//
//    private void addGroupMember(PoiGroup group, Map<String, PoiGroupMember> memberMap) {
//        update(QueryBuilders.boolQuery()
//                        .must(QueryBuilders.termQuery(ESPoiConstant.FIELD_PROJECT_ID, group.getProductId()))
//                        .must(QueryBuilders.termsQuery(ESPoiConstant.FIELD_POI_ID, memberMap.keySet())),
//                esPoi -> {
//                    ESGroup esGroup = new ESGroup();
//                    esGroup.setId(group.getId().toString());
//                    esGroup.setName(group.getName());
//                    esGroup.setNameEn(group.getEn());
//                    esGroup.setType(group.getType().toString());
//                    esGroup.setPriority(group.getPriority());
//                    PoiGroupMember m = memberMap.get(esPoi.getPoiId());
//                    esGroup.setMemberId(m.getId().toString());
//                    esGroup.setMemberPriority(m.getPriority());
//                    if (Objects.isNull(esPoi.getGroups())) {
//                        esPoi.setGroups(Arrays.asList(esGroup));
//                    } else {
//                        esPoi.getGroups().add(esGroup);
//                    }
//                });
//    }
//
//    private void update(QueryBuilder queryBuilder, Consumer<ESPoi> consumer) {
//        List<ESPoi> toUpdate = null;
//
//        int start = 0;
//        int maxPageSize = 10000;
//        long total = maxPageSize;
//        do {
//            NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
//            nativeSearchQueryBuilder.withQuery(queryBuilder);
//            nativeSearchQueryBuilder.withPageable(PageRequest.of(start / maxPageSize, maxPageSize));
//            SearchHits<ESPoi> searchHits = template.search(nativeSearchQueryBuilder.build(), ESPoi.class);
//            total = searchHits.getTotalHits();
//            if (Objects.isNull(toUpdate)) { toUpdate = new ArrayList<>(CollectionUtil.countInitialCapacity((int)total)); }
//            for (org.springframework.data.elasticsearch.core.SearchHit<ESPoi> searchHit : searchHits.getSearchHits()) {
//                consumer.accept(searchHit.getContent());
//                toUpdate.add(searchHit.getContent());
//            }
//
//            start += maxPageSize;
//        } while (start < total);
//
//        if (CollectionUtil.nonEmpty(toUpdate)) {
//            repository.saveAll(toUpdate);
//        }
//    }
//
//    private String id(String projectId, String poiId) {
//        return String.join(":", projectId, poiId);
//    }
//
//    /**
//     * 1.汉字取首字母
//     * 2.取大写字母
//     * 3.取小字母:
//     *     开头的小字母
//     *     特殊字符后的小字母
//     *     空格后的小字母
//     * @param str
//     * @return
//     */
//    private String getShortPinyin(String str) {
//        StringBuilder sb = new StringBuilder();
//        boolean appendLowercaseLetter = true;
//        for (int i = 0; i < str.length(); i++) {
//            char a = str.charAt(i);
//            if (ChineseHelper.isChinese(a)) {
//                Optional.ofNullable(PinyinHelper.convertToPinyinArray(a, PinyinFormat.WITHOUT_TONE))
//                        .filter(s -> s.length > 0)
//                        .map(s -> s[0])
//                        .filter(StringUtils::hasText)
//                        .map(s -> s.charAt(0))
//                        .ifPresent(sb::append);
//                appendLowercaseLetter = true;
//            } else if (Character.isUpperCase(a)) {
//                sb.append(a);
//                appendLowercaseLetter = false;
//            } else if (Character.isLowerCase(a)) {
//                if (appendLowercaseLetter) {
//                    sb.append(a);
//                    appendLowercaseLetter = false;
//                }
//            } else {
//                appendLowercaseLetter = true;
//            }
//        }
//        return sb.toString().toLowerCase();
//    }
//
//    private static String getFirstLetter(String pinyinInitials) {
//        return StringUtils.hasText(pinyinInitials) ? pinyinInitials.substring(0, 1) : null;
//    }
//
//    private Map<String, String> getSynonymMap(String buildingId, String token) {
//        SynonymResponse response = mapSDKClient.getSynonym(buildingId, token);
//        if (!response.isSuccess()) {
//            return Collections.emptyMap();
//        }
//
//        return response.getPolyPhone()
//                .stream()
//                .filter(s -> Objects.equals(1, s.getStatus()))
//                .collect(Collectors.toMap(SynonymInfo::getId, SynonymInfo::getContent, (o, n) -> n));
//    }
//
//    private Map<String, PoiType> getPoiTypeParentMap(String buildingId, String token) {
//        PoiTypeResponse response = mapSDKClient.getPoiTypes(AuthorizationRequest.builder()
//                .buildingId(buildingId)
//                .token(token)
//                .build());
//        if (!response.isSuccess()) {
//            return Collections.emptyMap();
//        }
//
//        Map<String, PoiType> poiTypeMap = new HashMap<>(512);
//        fillPoiTypeParentMap(response.getDatas(), poiTypeMap, null);
//        return poiTypeMap;
//    }
//
//    private void fillPoiTypeParentMap(List<PoiType> poiTypes, Map<String, PoiType> poiTypeMap, PoiType parent) {
//        if (CollectionUtil.nonEmpty(poiTypes)) {
//            for (int i = 0; i < poiTypes.size(); i++) {
//                PoiType poiType = poiTypes.get(i);
//                if (Objects.nonNull(parent)) {
//                    poiTypeMap.put(poiType.getValue(), parent);
//                }
//                fillPoiTypeParentMap(poiType.getChildren(), poiTypeMap, poiType);
//            }
//        }
//    }
//
//    private Map<String, List<ESKeywordRecommend>> getPoiKeywordRecommendMap(String projectId) {
//        List<RankingRecommendationEntity> entities = rankingRecommendationRepository.findAllByProjectId(projectId);
//        Map<Long, ESKeywordRecommend> keyWordRecommendMap = entities.stream()
//                .collect(Collectors.toMap(RankingRecommendationEntity::getId, this::build));
//        List<RankingRecommendationPoiEntity> poiEntities = rankingRecommendationPoiRepository.findAllByProjectId(projectId);
//        return poiEntities.stream()
//                .filter(p -> keyWordRecommendMap.containsKey(p.getId().getRankingRecommendationId()))
//                .collect(Collectors.groupingBy(p -> p.getId().getPoiId(),
//                        Collectors.mapping(p -> build(p, keyWordRecommendMap.get(p.getId().getRankingRecommendationId())), Collectors.toList())));
//    }
//
//    private ESKeywordRecommend build(RankingRecommendationEntity e) {
//        return new ESKeywordRecommend(e.getId().toString(), Optional.ofNullable(e.getKeyWords())
//                .filter(Objects::nonNull)
//                .map(Arrays::asList)
//                .orElseGet(Collections::emptyList));
//    }
//
//    private ESKeywordRecommend build(RankingRecommendationPoiEntity p, ESKeywordRecommend e) {
//        ESKeywordRecommend result = new ESKeywordRecommend();
//        result.setId(e.getId());
//        result.setKeywords(e.getKeywords());
//        result.setPriority(p.getPriority());
//        return result;
//    }
//
//    private Map<String, ShopActivity> getShopActivityMap(String projectId, String buildingId) {
//        return shopActivityService.storeActivityIsEnable(projectId, buildingId) ?
//                shopActivityService.apiStoreActivityList(projectId, buildingId)
//                        .stream()
//                        .filter(e -> Objects.nonNull(e.getPoiId()))
//                        .collect(Collectors.toMap(ShopActivity::getPoiId, Function.identity())) :
//                Collections.emptyMap();
//    }
//
//    private ESShopActivity build(ShopActivity activity) {
//        ESShopActivity result = new ESShopActivity();
//        result.setId(activity.getId());
//        result.setContent(buildShopActivityItems(activity.getActivityType()));
//        return result;
//    }
//
//    private List<ESShopActivity.Item> buildShopActivityItems(String s) {
//        if (!StringUtils.hasText(s)) {
//            return Collections.emptyList();
//        }
//        JsonNode node = JSON.parse(s, JsonNode.class);
//        List<ESShopActivity.Item> result = new ArrayList<>(CollectionUtil.countInitialCapacity(node.size()));
//        for (int i = 0; i < node.size(); i++) {
//            JsonNode item = node.get(i);
//            if (item.get("disabled").asBoolean()) {
//                continue;
//            }
//            ESShopActivity.Item aItem = new ESShopActivity.Item();
//            if (item.get("custom").asBoolean()) {
//                aItem.setName(getString(item.get("title")));
//                aItem.setNameEn(getString(item.get("titleEn")));
//                aItem.setType(getString(item.get("type")));
//                aItem.setTypeEn(getString(item.get("typeEn")));
//            } else {
//                aItem.setName(getString(item.get("tip")));
//                aItem.setNameEn(getString(item.get("tipEn")));
//                aItem.setType(getString(item.get("label")));
//                aItem.setTypeEn(getString(item.get("labelEn")));
//            }
//            result.add(aItem);
//        }
//        return result;
//    }
//
//    private String getString(JsonNode node) {
//        return Optional.ofNullable(node)
//                .map(JsonNode::asText)
//                .orElse(null);
//    }
//
//    private Map<String, List<ESGroup>> getPoiRecommendMap(String projectId, String buildingId) {
//        List<Recommend> recommends = recommendMapper.selectList(new QueryWrapper<Recommend>()
//                .select("id", "name", "name_en", "recommend_type", "priority")
//                .eq("product_id", projectId)
//                .eq("is_open", 1));
//        if (CollectionUtil.isEmpty(recommends)) {
//            return Collections.emptyMap();
//        }
//        Map<Long, Recommend> recommendMap = recommends.stream()
//                .collect(Collectors.toMap(Recommend::getId, Function.identity()));
//        List<RecommendPoi> recommendPois = recommendPoiMapper.selectList(new QueryWrapper<RecommendPoi>()
//                .select("id", "recommend_id","poi_id", "priority")
//                .in("recommend_id", recommendMap.keySet()));
//        long poiCount = recommendPois.stream()
//                .map(RecommendPoi::getPoiId)
//                .filter(StringUtils::hasText).distinct()
//                .count();
//        Map<String, List<ESGroup>> result = new HashMap<>(CollectionUtil.countInitialCapacity((int)poiCount));
//        for (int i = 0; i < recommendPois.size(); i++) {
//            RecommendPoi p = recommendPois.get(i);
//            Recommend r = recommendMap.get(p.getRecommendId());
//            if (Objects.isNull(r)) {
//                continue;
//            }
//
//            ESGroup g = new ESGroup();
//            g.setId(r.getId().toString());
//            g.setName(r.getName());
//            g.setNameEn(r.getNameEn());
//            g.setType(r.getRecommendType().toString());
//            g.setPriority(r.getPriority());
//            g.setMemberId(p.getId().toString());
//            g.setMemberPriority(p.getPriority());
//
//            result.computeIfAbsent(p.getPoiId(), k -> new ArrayList<>(recommends.size()))
//                    .add(g);
//        }
//        return result;
//    }
//
//    private Map<String, List<ESGroup>> getPoiGroupMap(String projectId, String buildingId) {
//        List<PoiGroup> groups = groupMapper.selectList(new QueryWrapper<PoiGroup>()
//                .select("id", "name", "en", "type", "priority")
//                .eq("product_id", projectId)
//                .eq("activated", 1));
//        if (CollectionUtil.isEmpty(groups)) {
//            return Collections.emptyMap();
//        }
//        Map<Long, PoiGroup> groupMap = groups.stream()
//                .collect(Collectors.toMap(PoiGroup::getId, Function.identity()));
//        List<PoiGroupMember> groupMembers = groupMemberMapper.selectList(new QueryWrapper<PoiGroupMember>()
//                .select("id", "group_id","poi_id", "priority")
//                .in("group_id", groupMap.keySet()));
//        long poiCount = groupMembers.stream()
//                .map(PoiGroupMember::getPoiId)
//                .filter(StringUtils::hasText)
//                .distinct()
//                .count();
//        Map<String, List<ESGroup>> result = new HashMap<>(CollectionUtil.countInitialCapacity((int)poiCount));
//        for (int i = 0; i < groupMembers.size(); i++) {
//            PoiGroupMember p = groupMembers.get(i);
//            PoiGroup r = groupMap.get(p.getGroupId());
//            if (Objects.isNull(r)) {
//                continue;
//            }
//
//            ESGroup g = new ESGroup();
//            g.setId(r.getId().toString());
//            g.setName(r.getName());
//            g.setNameEn(r.getEn());
//            g.setType(r.getType().toString());
//            g.setPriority(r.getPriority());
//            g.setMemberId(p.getId().toString());
//            g.setMemberPriority(p.getPriority());
//
//            result.computeIfAbsent(p.getPoiId(), k -> new ArrayList<>(groups.size()))
//                    .add(g);
//        }
//        return result;
//    }
//
//    private Map<String, Integer> getKeywordRecommendPoiPriorityMap(Long recommendId, List<String> poiIds) {
//        return rankingRecommendationPoiRepository.findAllById(poiIds.stream()
//                .map(poiId -> new RankingRecommendationPoiId(recommendId, poiId))
//                .collect(Collectors.toList()))
//                .stream()
//                .collect(Collectors.toMap(p -> p.getId().getPoiId(), RankingRecommendationPoiEntity::getPriority));
//    }

    private interface KeyQueryBuilder {
        void build(BoolQueryBuilder b, String key, String lang, Map<String, Float> boostMap);
    }
}
